🔧 Step 6: Full CRUD Integration Using Axios
- ✅ Create
- ✅ Read (already done)
- ✅ Update
- ✅ Delete
We'll build a clean and modular API service layer and integrate it step-by-step in the frontend.
🎯 Objective:
Use Axios to connect your frontend with the RESTful API and handle:
GET /tasks
→ fetch all tasksPOST /tasks
→ create taskPUT /tasks/:id
→ update taskDELETE /tasks/:id
→ delete task
📁 A. Update Task Service
Update src/lib/taskService.ts
:
import { api } from "./api"
export type Task = {
id: string
title: string
}
// Get all tasks
export async function fetchTasks(): Promise<Task[]> {
const res = await api.get("/tasks")
return res.data
}
// Create a new task
export async function createTask(title: string): Promise<Task> {
const res = await api.post("/tasks", { title })
return res.data
}
// Update a task
export async function updateTask(id: string, title: string): Promise<Task> {
const res = await api.put(`/tasks/${id}`, { title })
return res.data
}
// Delete a task
export async function deleteTask(id: string): Promise<void> {
await api.delete(`/tasks/${id}`)
}
🔁 Adjust the route paths (e.g.,
/tasks
,/api/tasks
) depending on your actual backend from GitLab.
✅ B. Update TaskPage.tsx
to Use Full CRUD
Update TaskPage.tsx
to integrate the service:
import { useEffect, useState } from "react"
import TaskTable from "@/components/task/TaskTable"
import {
fetchTasks,
createTask,
updateTask,
deleteTask,
Task,
} from "@/lib/taskService"
export default function TaskPage() {
const [tasks, setTasks] = useState<Task[]>([])
const [loading, setLoading] = useState(true)
const [editing, setEditing] = useState<{ id: string; title: string } | null>(null)
const [input, setInput] = useState("")
useEffect(() => {
getAllTasks()
}, [])
const getAllTasks = async () => {
try {
const data = await fetchTasks()
setTasks(data)
} catch (err) {
console.error("Fetch error:", err)
} finally {
setLoading(false)
}
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (editing) {
const updated = await updateTask(editing.id, input)
setTasks((prev) =>
prev.map((t) => (t.id === editing.id ? updated : t))
)
setEditing(null)
} else {
const newTask = await createTask(input)
setTasks((prev) => [newTask, ...prev])
}
setInput("")
}
const handleEdit = (id: string, title: string) => {
setEditing({ id, title })
setInput(title)
}
const handleDelete = async (id: string) => {
await deleteTask(id)
setTasks((prev) => prev.filter((t) => t.id !== id))
}
return (
<div className="max-w-4xl mx-auto mt-10 space-y-6">
<h1 className="text-3xl font-bold text-center">📋 Task Manager</h1>
{/* FORM */}
<form onSubmit={handleSubmit} className="flex gap-2 mb-4">
<input
className="border px-3 py-2 w-full rounded"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter task title"
/>
<button
className="bg-blue-600 text-white px-4 py-2 rounded"
type="submit"
>
{editing ? "Update" : "Add"}
</button>
</form>
{/* TABLE */}
{loading ? (
<p className="text-center text-gray-500">Loading tasks...</p>
) : (
<TaskTable tasks={tasks} onEdit={handleEdit} onDelete={handleDelete} />
)}
</div>
)
}
🛠️ Update TaskTable.tsx
to Accept onEdit
and onDelete
type Task = {
id: string
title: string
}
type TaskTableProps = {
tasks: Task[]
onEdit: (id: string, title: string) => void
onDelete: (id: string) => void
}
export default function TaskTable({ tasks, onEdit, onDelete }: TaskTableProps) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]">#</TableHead>
<TableHead>Title</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{tasks.length === 0 ? (
<TableRow>
<TableCell colSpan={3} className="text-center py-6 text-gray-500">
No tasks found.
</TableCell>
</TableRow>
) : (
tasks.map((task, index) => (
<TableRow key={task.id}>
<TableCell>{index + 1}</TableCell>
<TableCell>{task.title}</TableCell>
<TableCell className="text-right space-x-2">
<button
className="text-sm text-blue-600 hover:underline"
onClick={() => onEdit(task.id, task.title)}
>
Edit
</button>
<button
className="text-sm text-red-500 hover:underline"
onClick={() => onDelete(task.id)}
>
Delete
</button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
)
}
✅ Summary
You now have full CRUD operations wired up:
- ✅
GET
— fetch tasks - ✅
POST
— create new task - ✅
PUT
— update a task - ✅
DELETE
— remove a task
And all of this is done with Axios, ShadCN UI, and a modular service layer.